#!/bin/bash

# LifeVerse Platform 自动回滚脚本
# 支持多种回滚策略和故障恢复机制

set -e

# 配置变量
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
BACKUP_DIR="/opt/lifeverse/backups"
LOG_FILE="/var/log/lifeverse/rollback.log"
ROLLBACK_TIMEOUT=300  # 5分钟超时

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 日志函数
log() {
    echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
}

error() {
    echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
}

success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
}

warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}

# 显示帮助信息
show_help() {
    cat << EOF
LifeVerse Platform 自动回滚脚本

用法: $0 [选项] <环境> [目标版本]

环境:
    dev         开发环境
    test        测试环境
    prod        生产环境

选项:
    -h, --help              显示帮助信息
    -f, --force             强制回滚，跳过确认
    -t, --type <类型>       回滚类型 (auto|manual|emergency)
    -r, --reason <原因>     回滚原因
    -c, --check             仅检查回滚可行性
    -v, --verbose           详细输出
    --dry-run              模拟运行，不执行实际回滚
    --timeout <秒>         回滚超时时间（默认300秒）

回滚类型:
    auto        自动回滚（CI/CD触发）
    manual      手动回滚（运维人员触发）
    emergency   紧急回滚（系统故障）

示例:
    $0 prod                                    # 回滚生产环境到上一个版本
    $0 prod v1.2.2                           # 回滚生产环境到指定版本
    $0 --type emergency prod                  # 紧急回滚生产环境
    $0 --check prod                          # 检查生产环境回滚可行性
    $0 --force --reason "critical_bug" prod  # 强制回滚并指定原因

EOF
}

# 解析命令行参数
parse_args() {
    ENVIRONMENT=""
    TARGET_VERSION=""
    FORCE=false
    ROLLBACK_TYPE="manual"
    ROLLBACK_REASON=""
    CHECK_ONLY=false
    VERBOSE=false
    DRY_RUN=false

    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--help)
                show_help
                exit 0
                ;;
            -f|--force)
                FORCE=true
                shift
                ;;
            -t|--type)
                ROLLBACK_TYPE="$2"
                shift 2
                ;;
            -r|--reason)
                ROLLBACK_REASON="$2"
                shift 2
                ;;
            -c|--check)
                CHECK_ONLY=true
                shift
                ;;
            -v|--verbose)
                VERBOSE=true
                shift
                ;;
            --dry-run)
                DRY_RUN=true
                shift
                ;;
            --timeout)
                ROLLBACK_TIMEOUT="$2"
                shift 2
                ;;
            -*)
                error "未知选项: $1"
                show_help
                exit 1
                ;;
            *)
                if [[ -z "$ENVIRONMENT" ]]; then
                    ENVIRONMENT="$1"
                elif [[ -z "$TARGET_VERSION" ]]; then
                    TARGET_VERSION="$1"
                else
                    error "过多的参数: $1"
                    show_help
                    exit 1
                fi
                shift
                ;;
        esac
    done

    # 验证参数
    if [[ -z "$ENVIRONMENT" ]]; then
        error "必须指定环境"
        show_help
        exit 1
    fi

    if [[ "$ENVIRONMENT" != "dev" && "$ENVIRONMENT" != "test" && "$ENVIRONMENT" != "prod" ]]; then
        error "无效的环境: $ENVIRONMENT"
        exit 1
    fi

    if [[ "$ROLLBACK_TYPE" != "auto" && "$ROLLBACK_TYPE" != "manual" && "$ROLLBACK_TYPE" != "emergency" ]]; then
        error "无效的回滚类型: $ROLLBACK_TYPE"
        exit 1
    fi
}

# 设置环境变量
setup_environment() {
    case $ENVIRONMENT in
        dev)
            COMPOSE_FILE="docker-compose.dev.yml"
            HEALTH_URL="https://dev.lifeverse.com/api/actuator/health"
            ;;
        test)
            COMPOSE_FILE="docker-compose.test.yml"
            HEALTH_URL="https://test.lifeverse.com/api/actuator/health"
            ;;
        prod)
            COMPOSE_FILE="docker-compose.prod.yml"
            HEALTH_URL="https://lifeverse.com/api/actuator/health"
            ;;
    esac

    export LIFEVERSE_ENVIRONMENT="$ENVIRONMENT"
}

# 获取当前运行版本
get_current_version() {
    local current_image
    current_image=$(docker-compose -f "$COMPOSE_FILE" images -q lifeverse 2>/dev/null | head -1)
    
    if [[ -n "$current_image" ]]; then
        docker inspect "$current_image" --format='{{index .RepoTags 0}}' 2>/dev/null | cut -d':' -f2
    else
        echo "unknown"
    fi
}

# 获取可用的备份版本
get_available_versions() {
    if [[ -d "$BACKUP_DIR" ]]; then
        ls -1 "$BACKUP_DIR" | grep "lifeverse-${ENVIRONMENT}-" | sort -r
    fi
}

# 验证目标版本
validate_target_version() {
    if [[ -z "$TARGET_VERSION" ]]; then
        # 获取最新的备份版本
        TARGET_VERSION=$(get_available_versions | head -1)
        if [[ -z "$TARGET_VERSION" ]]; then
            error "找不到可用的备份版本"
            return 1
        fi
        log "自动选择目标版本: $TARGET_VERSION"
    fi

    local backup_path="$BACKUP_DIR/$TARGET_VERSION"
    if [[ ! -d "$backup_path" ]]; then
        error "找不到目标版本的备份: $TARGET_VERSION"
        return 1
    fi

    if [[ ! -f "$backup_path/backup_info.json" ]]; then
        error "备份信息文件缺失: $backup_path/backup_info.json"
        return 1
    fi

    return 0
}

# 健康检查
health_check() {
    local max_attempts=10
    local attempt=1

    log "执行健康检查..."

    while [[ $attempt -le $max_attempts ]]; do
        if curl -f -s "$HEALTH_URL" > /dev/null 2>&1; then
            success "健康检查通过 (尝试 $attempt/$max_attempts)"
            return 0
        fi

        warning "健康检查失败 (尝试 $attempt/$max_attempts)，10秒后重试..."
        sleep 10
        ((attempt++))
    done

    error "健康检查失败，已尝试 $max_attempts 次"
    return 1
}

# 创建回滚前快照
create_pre_rollback_snapshot() {
    log "创建回滚前快照..."

    local snapshot_name="pre-rollback-${ENVIRONMENT}-$(date +%Y%m%d-%H%M%S)"
    local snapshot_path="$BACKUP_DIR/$snapshot_name"

    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将创建快照: $snapshot_path"
        return 0
    fi

    mkdir -p "$snapshot_path"

    # 记录当前状态
    local current_version
    current_version=$(get_current_version)
    
    cat > "$snapshot_path/snapshot_info.json" << EOF
{
    "environment": "$ENVIRONMENT",
    "timestamp": "$(date -Iseconds)",
    "current_version": "$current_version",
    "rollback_target": "$TARGET_VERSION",
    "rollback_type": "$ROLLBACK_TYPE",
    "rollback_reason": "$ROLLBACK_REASON",
    "snapshot_type": "pre_rollback"
}
EOF

    # 备份当前容器状态
    docker-compose -f "$COMPOSE_FILE" ps > "$snapshot_path/containers.txt" 2>/dev/null || true
    docker-compose -f "$COMPOSE_FILE" logs --tail=1000 > "$snapshot_path/logs.txt" 2>/dev/null || true

    success "快照创建完成: $snapshot_path"
}

# 停止服务
stop_services() {
    log "停止服务..."

    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将停止服务"
        return 0
    fi

    # 优雅停止应用服务
    if [[ "$ENVIRONMENT" == "prod" ]]; then
        # 生产环境逐个停止实例
        docker-compose -f "$COMPOSE_FILE" stop lifeverse-1 || true
        sleep 10
        docker-compose -f "$COMPOSE_FILE" stop lifeverse-2 || true
    else
        docker-compose -f "$COMPOSE_FILE" stop lifeverse || true
    fi

    success "服务停止完成"
}

# 恢复数据
restore_data() {
    local backup_path="$BACKUP_DIR/$TARGET_VERSION"
    
    log "恢复数据从备份: $TARGET_VERSION"

    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将恢复数据从: $backup_path"
        return 0
    fi

    # 恢复数据库
    if [[ -f "$backup_path/database.sql" ]]; then
        log "恢复数据库..."
        
        # 确保数据库服务运行
        if [[ "$ENVIRONMENT" == "prod" ]]; then
            docker-compose -f "$COMPOSE_FILE" up -d mysql-master
            sleep 30
            docker-compose -f "$COMPOSE_FILE" exec -T mysql-master mysql -u root -p"$MYSQL_ROOT_PASSWORD" lifeverse_${ENVIRONMENT} < "$backup_path/database.sql"
        else
            docker-compose -f "$COMPOSE_FILE" up -d mysql
            sleep 30
            docker-compose -f "$COMPOSE_FILE" exec -T mysql mysql -u root -p"$MYSQL_ROOT_PASSWORD" lifeverse_${ENVIRONMENT} < "$backup_path/database.sql"
        fi
        
        success "数据库恢复完成"
    else
        warning "未找到数据库备份文件"
    fi

    # 恢复Redis数据
    if [[ -f "$backup_path/redis.rdb" ]]; then
        log "恢复Redis数据..."
        
        if [[ "$ENVIRONMENT" == "prod" ]]; then
            docker-compose -f "$COMPOSE_FILE" stop redis-master redis-slave-1 redis-slave-2
            docker cp "$backup_path/redis.rdb" redis-master-prod:/data/dump.rdb
            docker-compose -f "$COMPOSE_FILE" start redis-master redis-slave-1 redis-slave-2
        else
            docker-compose -f "$COMPOSE_FILE" stop redis
            docker cp "$backup_path/redis.rdb" redis-${ENVIRONMENT}:/data/dump.rdb
            docker-compose -f "$COMPOSE_FILE" start redis
        fi
        
        success "Redis数据恢复完成"
    else
        warning "未找到Redis备份文件"
    fi

    # 恢复配置文件
    if [[ -d "$backup_path/config" ]]; then
        log "恢复配置文件..."
        cp -r "$backup_path/config/"* "$PROJECT_ROOT/config/" 2>/dev/null || true
        success "配置文件恢复完成"
    fi
}

# 启动服务
start_services() {
    log "启动服务..."

    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将启动服务"
        return 0
    fi

    # 从备份信息中获取镜像标签
    local backup_path="$BACKUP_DIR/$TARGET_VERSION"
    local target_image
    target_image=$(jq -r '.current_image // "latest"' "$backup_path/backup_info.json" 2>/dev/null || echo "latest")
    
    export LIFEVERSE_IMAGE_TAG="$target_image"
    
    # 启动服务
    if [[ "$ENVIRONMENT" == "prod" ]]; then
        # 生产环境逐个启动实例
        docker-compose -f "$COMPOSE_FILE" up -d lifeverse-1
        sleep 30
        
        if health_check; then
            docker-compose -f "$COMPOSE_FILE" up -d lifeverse-2
        else
            error "第一个实例启动失败"
            return 1
        fi
    else
        docker-compose -f "$COMPOSE_FILE" up -d lifeverse
    fi

    # 等待服务完全启动
    log "等待服务启动..."
    sleep 60

    success "服务启动完成"
}

# 验证回滚结果
verify_rollback() {
    log "验证回滚结果..."

    # 健康检查
    if ! health_check; then
        error "回滚后健康检查失败"
        return 1
    fi

    # 验证版本
    local current_version
    current_version=$(get_current_version)
    log "当前运行版本: $current_version"

    # 功能测试
    log "执行功能测试..."
    if [[ "$DRY_RUN" == false ]]; then
        # 这里可以添加具体的功能测试
        curl -f -s "$HEALTH_URL/info" > /dev/null || {
            warning "功能测试失败"
            return 1
        }
    fi

    success "回滚验证通过"
    return 0
}

# 清理资源
cleanup() {
    log "清理资源..."

    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将清理资源"
        return 0
    fi

    # 清理未使用的镜像
    docker image prune -f > /dev/null 2>&1 || true

    # 清理未使用的容器
    docker container prune -f > /dev/null 2>&1 || true

    success "资源清理完成"
}

# 发送通知
send_notification() {
    local status=$1
    local message=$2

    if [[ -z "$NOTIFICATION_WEBHOOK" ]]; then
        return 0
    fi

    local color="good"
    case $status in
        "error") color="danger" ;;
        "warning") color="warning" ;;
    esac

    curl -X POST "$NOTIFICATION_WEBHOOK" \
        -H "Content-Type: application/json" \
        -d "{
            \"text\": \"LifeVerse Platform 回滚通知\",
            \"attachments\": [{
                \"color\": \"$color\",
                \"fields\": [{
                    \"title\": \"环境\",
                    \"value\": \"$ENVIRONMENT\",
                    \"short\": true
                }, {
                    \"title\": \"状态\",
                    \"value\": \"$message\",
                    \"short\": true
                }, {
                    \"title\": \"目标版本\",
                    \"value\": \"$TARGET_VERSION\",
                    \"short\": true
                }, {
                    \"title\": \"回滚类型\",
                    \"value\": \"$ROLLBACK_TYPE\",
                    \"short\": true
                }, {
                    \"title\": \"原因\",
                    \"value\": \"$ROLLBACK_REASON\",
                    \"short\": false
                }]
            }]
        }" 2>/dev/null || true
}

# 记录回滚历史
record_rollback_history() {
    local status=$1
    local history_file="$BACKUP_DIR/rollback_history.json"
    
    local record="{
        \"timestamp\": \"$(date -Iseconds)\",
        \"environment\": \"$ENVIRONMENT\",
        \"target_version\": \"$TARGET_VERSION\",
        \"rollback_type\": \"$ROLLBACK_TYPE\",
        \"rollback_reason\": \"$ROLLBACK_REASON\",
        \"status\": \"$status\",
        \"duration\": $(($(date +%s) - START_TIME))
    }"

    if [[ -f "$history_file" ]]; then
        # 添加到现有历史
        jq ". += [$record]" "$history_file" > "${history_file}.tmp" && mv "${history_file}.tmp" "$history_file"
    else
        # 创建新的历史文件
        echo "[$record]" > "$history_file"
    fi
}

# 执行回滚检查
perform_rollback_check() {
    log "执行回滚可行性检查..."

    # 检查备份可用性
    if ! validate_target_version; then
        error "目标版本验证失败"
        return 1
    fi

    # 检查服务状态
    local current_version
    current_version=$(get_current_version)
    log "当前版本: $current_version"
    log "目标版本: $TARGET_VERSION"

    # 检查依赖服务
    log "检查依赖服务状态..."
    if ! docker-compose -f "$COMPOSE_FILE" ps | grep -q "Up"; then
        warning "部分服务未运行"
    fi

    # 检查磁盘空间
    local available_space
    available_space=$(df "$BACKUP_DIR" | awk 'NR==2 {print $4}')
    if [[ $available_space -lt 1048576 ]]; then  # 1GB
        warning "磁盘空间不足，可能影响回滚操作"
    fi

    success "回滚可行性检查通过"
    return 0
}

# 主函数
main() {
    START_TIME=$(date +%s)
    
    # 创建日志目录
    mkdir -p "$(dirname "$LOG_FILE")"

    log "LifeVerse Platform 自动回滚脚本启动"
    log "回滚类型: $ROLLBACK_TYPE"
    log "回滚原因: ${ROLLBACK_REASON:-未指定}"

    parse_args "$@"
    setup_environment

    if [[ "$VERBOSE" == true ]]; then
        set -x
    fi

    # 仅检查模式
    if [[ "$CHECK_ONLY" == true ]]; then
        if perform_rollback_check; then
            success "回滚可行性检查通过"
            exit 0
        else
            error "回滚可行性检查失败"
            exit 1
        fi
    fi

    # 验证目标版本
    if ! validate_target_version; then
        error "目标版本验证失败"
        exit 1
    fi

    # 确认回滚操作
    if [[ "$FORCE" == false && "$ROLLBACK_TYPE" != "auto" ]]; then
        echo
        warning "即将执行回滚操作:"
        echo "  环境: $ENVIRONMENT"
        echo "  目标版本: $TARGET_VERSION"
        echo "  回滚类型: $ROLLBACK_TYPE"
        echo "  回滚原因: ${ROLLBACK_REASON:-未指定}"
        echo
        read -p "确认执行回滚? (yes/no): " -r
        if [[ ! $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then
            log "回滚操作已取消"
            exit 0
        fi
    fi

    # 设置超时
    (
        sleep "$ROLLBACK_TIMEOUT"
        error "回滚操作超时"
        kill $$
    ) &
    TIMEOUT_PID=$!

    # 执行回滚流程
    if create_pre_rollback_snapshot && \
       stop_services && \
       restore_data && \
       start_services && \
       verify_rollback; then
        
        kill $TIMEOUT_PID 2>/dev/null || true
        cleanup
        record_rollback_history "success"
        send_notification "success" "回滚成功"
        success "回滚操作完成"
        exit 0
    else
        kill $TIMEOUT_PID 2>/dev/null || true
        record_rollback_history "failed"
        send_notification "error" "回滚失败"
        error "回滚操作失败"
        exit 1
    fi
}

# 信号处理
trap 'error "回滚被中断"; kill $TIMEOUT_PID 2>/dev/null || true; exit 130' INT TERM

# 执行主函数
main "$@"